package filters

import (
	"gateway/lib"
	"strconv"
	"github.com/json-iterator/go"
	"github.com/funlake/gopkg/limiter"
	"strings"
	"gateway/conf"
	"github.com/valyala/fasthttp"
	"time"
	"gateway/types"
)
var tokenBucketSchedular = limiter.NewTokenBucketSchedular()
type RateLimitInfo struct {
	BucketKey string
	Second string `json:"s"`
	Minute string `json:"m"`
	Hour string `json:"h"`
	Day string `json:"d"`
}

func NewFilterRateLimit() *FilterRateLimit{
	return &FilterRateLimit{}
}
type FilterRateLimit struct{

}

func (frl *FilterRateLimit) Pre(proxyContext *types.ProxyContext,fasthttpCtx *fasthttp.RequestCtx)(bool,int,string){
	//用户访问令牌
	acctoken := proxyContext.GetToken()
	//对所有用户限流
	if acctoken == "" {
		acctoken = "__ALL__"
	}
	rl,isset:= getRateLimit(proxyContext,acctoken)
	//未发现任何限流设置，则直接穿过
	if !isset {
		return true,0,""
	}
	has := false
	rate := 0
	size := 0
	if has,rate,size = parseRateLimit(rl.Second);has{
		return allow(rl.BucketKey + "_second",rate,size,1),conf.REQUEST_RATE_LIMIT,"每秒请求次数超过限额:"+strconv.Itoa(size)+"/s"
	}
	if has,rate,size = parseRateLimit(rl.Minute);has{
		return allow(rl.BucketKey + "_minute",rate,size,60),conf.REQUEST_RATE_LIMIT,"每分钟请求次数超过限额:"+strconv.Itoa(size)+"/m"
	}
	if has,rate,size = parseRateLimit(rl.Hour);has{
		return allow(rl.BucketKey + "_hour",rate,size,3600),conf.REQUEST_RATE_LIMIT,"每小时请求次数超过限额:"+strconv.Itoa(size)+"/h"
	}
	if has,rate,size = parseRateLimit(rl.Day);has{
		cache := lib.NewCache()
		dayBucket := tokenBucketSchedular.GetTimeTokenBucket(rl.BucketKey + "_day",rate,size,86400,&limiter.DayRateFun{
			Get : func(key string) (string, error) {
				return cache.GetRateLimitLeft(key)
			},
			Set : func(k string, v string) {
				cache.SetRateLimitLeft(k,v)
			},
		})
		if !dayBucket.GetToken() {
			return false, conf.REQUEST_RATE_LIMIT,"每日请求次数超过限额"+strconv.Itoa(size)+"/d"
		}
	}
	//log.Success(rl.BucketKey+"限流通过")
	return true,0,""
}

func (frl *FilterRateLimit) Post(proxyContext *types.ProxyContext,fasthttpCtx *fasthttp.RequestCtx)(bool,int,string){
	return true,0,""
}
func parseRateLimit(s string)(bool,int,int){
	if s == ""{
		return false,-1,-1
	}
	rate,size := 0,0
	rlm := strings.Split(s, ",")
	if len(rlm) > 0 {
		rate, _ = strconv.Atoi(strings.Trim(rlm[0]," "))
		size, _ = strconv.Atoi(strings.Trim(rlm[1]," "))
		if rate > 0 && size > 0 {
			return true,rate,size
		}
	}
	return false,-1,-1
}
func allow(bucketKey string,rate int ,qps int,second time.Duration) bool {
	hourBucket := tokenBucketSchedular.GetTimeTokenBucket(bucketKey,rate,qps,second,nil)
	if !hourBucket.GetToken() {
		return false
	}
	return true
}
func getRateLimit(proxy *types.ProxyContext,token string) (*RateLimitInfo,bool) {
	appjson,err := proxy.GetAppInfo()
	key := ""
	if err != nil {
		//beego.Warning("No app info ,probably a common api for all people")
		key = token+"_"+proxy.Mapkey
		//return nil,false
	}else{
		key = appjson.Appid+"_"+proxy.Mapkey
	}
	cache := lib.NewCache()

	if v,e := cache.GetRateLimit(key);e ==nil {
		//beego.Warning(v)
		ratelimit := & RateLimitInfo{BucketKey:key}
		//ratelimit.BucketKey = key
		err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(v),ratelimit)
		if err != nil {
			return nil,false
		}
		return ratelimit ,true
	}
	return nil,false

}